#ifndef RENDER_CONTEXT_H
#define RENDER_CONTEXT_H

#include "Ray.h"
#include "DeviceList.h"

namespace RayTracePro
{
	struct SortGroup
	{
		int GroupId;
		int Base, Count;
		int ScannedCount;
	};

	class GeometryInstance
	{
	public:
		Matrix4 TransformInverse, Transform;
	};

	struct TraverseResult
	{
		int LeafId, MeshId;
		union
		{
			struct
			{
				unsigned int stackPtr:8;
				unsigned int nodeId:24;

			};
			int Flags;
		};
		
		
		float tMin;
		union
		{
			struct
			{
				unsigned char Stack[8];
			};
			struct
			{
				unsigned int Stack0, Stack1;
			};
		};
		
	};

	struct ShadingBuffer
	{
		friend struct RenderContext;
	private:
		RenderContext * context;
		int allocationId;
		DeviceList<float> * buffer;
		void Free();
	public:
		ShadingBuffer()
			:context(0), allocationId(-1), buffer(0)
		{}
		ShadingBuffer(RenderContext * renderContext, int allocId, DeviceList<float> * buffer);
		~ShadingBuffer()
		{
			Free();
		}
		ShadingBuffer(ShadingBuffer && _buffer)
			:context(0), allocationId(-1), buffer(0)
		{
			*this = _buffer;
		}
		ShadingBuffer & operator = (ShadingBuffer && _buffer)
		{
			Free();
			context = _buffer.context;
			allocationId = _buffer.allocationId;
			buffer = _buffer.buffer;
			_buffer.context = 0;
			_buffer.allocationId = -1;
			_buffer.buffer = 0;
		}
	
		DeviceList<float> & GetBuffer()
		{
			return *buffer;
		}
	};

	struct RenderContext
	{
	private:
		static const int MaxShadingBuffers = 16;
		DeviceList<float> shadingBuffers[MaxShadingBuffers];
		bool shadingBufferAllocationState[MaxShadingBuffers];
	public:
		DeviceList<Vec2> PixelPositions;
		DeviceList<Ray> Rays;
		DeviceList<RayDifferential> RayDiffs;
		DeviceList<DifferentialGeometry> Intersections;
		DeviceList<Vec4> IntermediateIntersections;
		DeviceList<Vec4> Colors;
		DeviceList<float> RandX;
		DeviceList<float> RandY;
		DeviceList<TraverseResult> TraverseContinuation;
		DeviceList<int> HeadFlags, ScanHeadFlags;
		DeviceList<SortGroup> Groups;
		DeviceList<int> skeleton;
		ShadingBuffer AllocShadingBuffer()
		{
			ShadingBuffer rs;
			for (int i = 0; i<MaxShadingBuffers; i++)
			{
				if (shadingBufferAllocationState[i] == false)
				{
					rs.allocationId = i;
					rs.buffer = shadingBuffers + i;
					rs.context = this;
					shadingBufferAllocationState[i] = true;
					return rs;
				}
			}
			throw "Run out of shading buffers";
		}
		void FreeShadingBuffer(int allocId)
		{
			if (allocId >= 0 && allocId < MaxShadingBuffers)
				shadingBufferAllocationState[allocId] = false;
		}
		RenderContext();
	};
}

#endif